package com.example.gobang_web_system.api;

import com.example.gobang_web_system.game.MatchRequest;
import com.example.gobang_web_system.game.MatchResponse;
import com.example.gobang_web_system.game.Matcher;
import com.example.gobang_web_system.game.OnlineUserManager;
import com.example.gobang_web_system.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

// 通过这个类来处理匹配功能中的 websocket 请求
@Component
public class MatchAPI extends TextWebSocketHandler {
    // 此处需要保存用户的 上线/离线 状态；目的是为了能够在代码中比较方便的获取到某个用户当前的 websocket 会话，
    // 从而可以通过这个会话来给这个客户端发送信息，同时可以感知他的 在线/离线 状态

    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private Matcher matcher;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 玩家上线，加入到 OnlineUserManager 中
        /**
         * 1.先获取到当前用户的身份信息(谁在游戏大厅中建立的连接)
         *   此处的代码，之所以能够 getAttributes，全靠在注册 websocket 的时候
         *   加上的 .addInterceptors(new HttpSessionHandshakeInterceptor());
         *   这个逻辑就把 HttpSession 中的 Attribute 都拿给了 WebSocketSession 中了
         *   在 Http 登录逻辑中，往 HttpSession 中存了 User 数据:  httpSession.setAttribute("user", user);
         *   此时就可以在 WebSocketSession 中把之前 HttpSession 里存的 User 对象给拿到了
         *
         * 注意，此处拿到的 user，是有可能为空的！
         * 如果之前用户压根就没有通过 HTTP 来进行登录，直接通过 /game_hall.html
         * 这个 url 来访问游戏大厅页面，此时就会出现 user 为 null 的情况
         */
        try {
            User user = (User) session.getAttributes().get("user");

            // 2. 先判定当前用户是否已经登陆过(已经是在线状态)，如果是已经在线，就不该继续进行后续逻辑
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(user.getUserId());
            WebSocketSession tmpSession2 = onlineUserManager.getFromGameRoom(user.getUserId());
            if(tmpSession != null || tmpSession2 != null) {
                // 当前用户已经登陆过了！
                // 针对这个情况要告知客户端，你这里重复登录了
                MatchResponse response = new MatchResponse();
                response.setOk(true);
                response.setReason("当前禁止多开");
                response.setMessage("repeatConnection");
                session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
                // session.close();
                return;
            }

            // 3. 拿到身份信息之后，就可以把玩家设置成在线状态
            onlineUserManager.enterGameHall(user.getUserId(), session);
            System.out.println("玩家 " + user.getUsername() + " 进入游戏大厅！");
        } catch (NullPointerException e) {
            System.out.println("[MatchAPI.afterConnectionEstablished] 当前用户未登录!");
            //e.printStackTrace();
            // 出现空指针异常，说明当前用户的身份信息是空的，用户还没有登录
            // 就把当前用户尚未登陆这个信息返回回去
            MatchResponse response = new MatchResponse();
            response.setOk(false);
            response.setReason("您尚未登陆！不能进入后续匹配功能！");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 实现处理开始匹配请求和处理停止匹配请求
        User user = (User) session.getAttributes().get("user");
        // 获取到客户端给服务器发送的数据
        String payload = message.getPayload();
        // 当前这个数据载荷是一个JSON格式的字符串，就需要把它转成Java对象
        MatchRequest request = objectMapper.readValue(payload, MatchRequest.class);
        MatchResponse response = new MatchResponse();
        if(request.getMessage().equals("startMatch")) {
            // 进入匹配队列
            matcher.add(user);
            // 把玩家信息放入匹配队列之后，就可以返回一个响应给客户端了
            response.setOk(true);
            response.setMessage("startMatch");
        } else if(request.getMessage().equals("stopMatch")) {
            // 退出匹配队列
            matcher.remove(user);
            // 移除之后，就可以返回一个响应给客户端了
            response.setOk(true);
            response.setMessage("stopMatch");
        } else {
            response.setOk(false);
            response.setReason("非法的匹配请求");
        }
        String jsonString = objectMapper.writeValueAsString(response);
        session.sendMessage(new TextMessage(jsonString));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        try {
            // 玩家下线，从 OnlineUserManager 中删除
            User user = (User) session.getAttributes().get("user");
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(user.getUserId());
            // 出发到此操作，传入的 serrion 对象和 保存的 session 对象相同，就进行删除
            if(tmpSession == session) {
                onlineUserManager.exitGameHall(user.getUserId());
            }
            // 如果玩家在匹配中，而 websocket 连接断开了，就应该移除匹配队列
            matcher.remove(user);
        } catch (NullPointerException e) {
            System.out.println("[MatchAPI.handleTransportError] 当前用户未登录!");
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        try {
            // 玩家下线，从 OnlineUserManager 中删除
            User user = (User) session.getAttributes().get("user");
            WebSocketSession tmpSession = onlineUserManager.getFromGameHall(user.getUserId());
            // 出发到此操作，传入的 serrion 对象和 保存的 session 对象相同，就进行删除
            if(tmpSession == session) {
                onlineUserManager.exitGameHall(user.getUserId());
            }
            matcher.remove(user);
        } catch (NullPointerException e) {
            System.out.println("[MatchAPI.afterConnectionClosed] 当前用户未登录!");
        }
    }
}
